package com.sc.byahi.user.client;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sc.byahi.user.client.components.Pager;
import com.sc.byahi.user.shared.Profile;
import com.sc.byahi.user.shared.constants.Config;

public class ProfileListView extends ProfileResultView implements ICleaner {
//	private static final Logger logger = Logger.getLogger("ProfileListView");
	private FlexTable contactsTable = new FlexTable();
	private Pager pagerView;
	private VerticalPanel vertPanel = new VerticalPanel();
	private List<ProfileViewDiv> widgetCache = new ArrayList<ProfileViewDiv>(Config.resultPageSize);

	public ProfileListView() {
		initWidget(vertPanel);
		vertPanel.setWidth("100%");
	}

	@Override
	public void clean() {
		vertPanel.clear();
		collectViewsToCache();
		super.clean();
	}

	/**
	 * Redraws whole view, including paginator part
	 */
	public void redrawView() {
		pagerView = new Pager(data, isMap, this);
		vertPanel.add(pagerView);
		vertPanel.add(contactsTable);
		redrawTablePage();
	}

	private List<Long> findUncachedIds(List<Long> currIds){
		List<Long> toBeFetched = new ArrayList<Long>();
		for (Long id : currIds) {
			if (!currProfiles.containsKey(id)) {
				toBeFetched.add(id);
			}
		}
		return toBeFetched;
	}
	
	public void redrawTablePage() {
		final List<Long> currIds = pagerView.getPage();
		List<Long> toBeFetched = findUncachedIds(currIds);
		// by the time data is being fetched,
		// So that all widgets are returned to cache and maps and everything else is cleaned.
		collectViewsToCache();
		final AsyncCallback<List<Profile>> prefetchHandler = new AsyncCallback<List<Profile>>() {
			@Override
			public void onSuccess(List<Profile> result) {
				for (Profile p : result) {
					currProfiles.put(p.getId(), p);
				}
			}
			
			@Override
			public void onFailure(Throwable caught) {
			}
		};

		final AsyncCallback<List<Profile>> handler = new AsyncCallback<List<Profile>>() {

			@Override
			public void onSuccess(List<Profile> result) {
				int index = 0;
				for (Profile p : result) {
					currProfiles.put(p.getId(), p);
				}
				for (Long long1 : currIds) {
					Profile p = (Profile) currProfiles.get(long1);
					ProfileViewDiv profileView = getFromCache(ownData);
					contactsTable.setWidget(index++, 0, profileView);
					profileView.showData(p);
				}
				prefetch: {
					List<Long> prefetched = pagerView.prefetchNextPage();
					List<Long> toBeFetched = findUncachedIds(prefetched);
					GreetingService.Util.getInstance().findPublicProfilesByIds(toBeFetched, prefetchHandler);
				}
			}

			@Override
			public void onFailure(Throwable caught) {
				UIUtil.showGenericFailureDialog();
			}
		};
		if (ownData) {
			GreetingService.Util.getInstance().findMyProfilesById(currIds, handler);
		} else {
			GreetingService.Util.getInstance().findPublicProfilesByIds(toBeFetched, handler);
		}
	}

	public void redrawProfile(Profile p) {
		if (super.currProfiles.containsKey(p.getId())) {
			super.currProfiles.put(p.getId(), p);
		}
		for (int i = 0; i < contactsTable.getRowCount(); i++) {
			ProfileViewDiv view = (ProfileViewDiv) contactsTable.getWidget(i, 0);
			if (view.getP().getId().equals(p.getId())) {
				view.cleanForReuse();
				view.showData(p);
			}
		}
	}
	@Deprecated
	private ProfileViewDiv getProfileView(Long profileId) {
		for (int i = 0; i < contactsTable.getRowCount(); i++) {
			ProfileViewDiv view = (ProfileViewDiv) contactsTable.getWidget(i, 0);
			if (view.getP().getId().equals(profileId)) {
				return view;
			}
		}
		return null;
	}

	/**
	 * After hiding, profiles should shift up, so one profile should be added in the last row.
	 * 
	 * @param id
	 * @deprecated
	 */
	public void addProfile(Long id) {
		if (!ownData) {// own data can bnot be rated, so this method must never be called for own data.
			GreetingService.Util.getInstance().findPublicProfilesByIds(Arrays.asList(id), new AsyncCallback<List<Profile>>() {
				@Override
				public void onSuccess(List<Profile> result) {
					for (Profile p : result) {
						ProfileViewDiv profileView = getFromCache(ownData);
						int rowCount = contactsTable.getRowCount();
						contactsTable.setWidget(rowCount, 0, profileView);
						profileView.showData(p);
						if (currProfiles.containsKey(p.getId())) {
							currProfiles.put(p.getId(), p);
						}
					}
				}

				@Override
				public void onFailure(Throwable caught) {
				}
			});
		}
	}

	public void hideProfile(Long id) {
		// remove profile data and remove it from pager too. update with new paging values.
		for (int i = 0; i < contactsTable.getRowCount(); i++) {
			Widget w = contactsTable.getWidget(i, 0);
			ProfileViewDiv view = (ProfileViewDiv) w;
			if ((view).getP().getId().equals(id)) {
				returnToCache(view);
				contactsTable.removeRow(i);
			}
		}
		// TODO: pager view should take care of refreshing itself with data
		pagerView.hideId(id);
		redrawTablePage();
	}

	private void collectViewsToCache() {
		for (int i = 0; i < contactsTable.getRowCount(); i++) {
			Widget w = contactsTable.getWidget(i, 0);
			returnToCache((ProfileViewDiv) w);// always profileViewDiv instance
		}
		contactsTable.removeAllRows();
	}

	private void returnToCache(ProfileViewDiv view) {
		if (widgetCache.size() > Config.resultPageSize) {
//			logger.log(Level.SEVERE, "ProfileViewDiv cache size is more than paging size");
		}
		view.cleanForReuse();
		widgetCache.add(view);
//		logger.log(Level.INFO, "ProfileViewDiv cache size is: " + widgetCache.size());
	}

	private ProfileViewDiv getFromCache(boolean ownData) {
		if (widgetCache.isEmpty()) {
			widgetCache.add(new ProfileViewDiv(isMap));
		}
		ProfileViewDiv w = widgetCache.remove(0);
		w.setOwnData(ownData);
//		logger.log(Level.INFO, "ProfileViewDiv cache size is: " + widgetCache.size());
		return w;
	}
	@Deprecated
	private void prepareCache(int size) {
		if (widgetCache.size() < size) {
			for (int i = widgetCache.size(); i < size; i++) {
				widgetCache.add(new ProfileViewDiv(isMap));
			}
		}
//		logger.log(Level.SEVERE, "Cache prepared with size: " + size);
	}
}
